/*
*  Copyright (c) 2006-2020, Chukie
*
*  SPDX-License-Identifier: Apache-2.0
*
*  @file     :
*
*  @brief    :
*  Website   : https://gitee.com/zcj20080882
*
*
*  Change Logs:
*  Date           Author          Notes
*  2021-02-05     zhaocj       The first version
*/
/* Includes -----------------------------------------------------------------*/
#include <sms_pdu.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LOG_TAG     "[SMS]"
#define LOG_LVL     DBG_LOG
#include <rtdbg.h>
/* Private typedef ----------------------------------------------------------*/
/* Private define -----------------------------------------------------------*/
#define SMS_RESP_HEADER             "+CMGL: "
#define SMS_RESP_EOF                "\r\n"
#define SMS_RESP_UHDI               (1 << 6)
/* Private variables --------------------------------------------------------*/
/* Extern variables ---------------------------------------------------------*/
/* Private function prototypes ----------------------------------------------*/
/******************************************************************************/
/*@描述: 将GSM字符转换为ASCII字符
 *
 *@参数: pStr
 *@返回:
 *@注意:
 *****************************************************************************/
char ConvertGSMToASCII(char character)
{
    //GSM中@、$、_的编码与ASCII中不对应
    char ret = character;
    switch (ret)
    {
    case 0x02: //$
        ret = 0x24;
        break;
    case 0x00: //@
        ret = 0x40;
        break;
    case 0x11: //_
        ret = 0x5F;
        break;
    default:
        break;
    }
    return ret;
}

/******************************************************************************/
/*@描述: 将GSM 7bit字节流解压为ASCII字节流
 *
 *@参数: gsm  待解压的GSM 7bit字节流
 *       ascii 存放解压后的ASCII字节流的缓冲区
 *       ascii_size 存放解压后的ASCII字节流的缓冲区大小
 *@返回: 解压后的ASCII字节流长度
 *@注意:
 *****************************************************************************/
int GSM7bit2ASCII(char *gsm, int gsm_len, char *ascii, int ascii_size)
{
    int nSrc;   // 源字符串的计数值
    int nDst;   // 目标解码串的计数值
    int nByte;  // 当前正在处理的组内字节的序号，范围是0-6
    char nLeft; // 上一字节残余的数据
    // 计数值初始化
    nSrc = 0;
    nDst = 0;
    // 组内字节序号和残余数据初始化
    nByte = 0;
    nLeft = 0;
    // 将源数据每7个字节分为一组，解压缩成8个字节
    // 循环该处理过程，直至源数据被处理完
    // 如果分组不到7字节，也能正确处理
    while (nSrc < gsm_len && nDst < ascii_size)
    {
        // 将源字节右边部分与残余数据相加，去掉最高位，得到一个目标解码字节
        ascii[nDst] = (char)(((gsm[nSrc] << nByte) | nLeft) & 0x7f);
        //ascii[nDst] = ConvertGSMToASCII(ascii[nDst]);
        // 将该字节剩下的左边部分，作为残余数据保存起来
        nLeft = (char)(gsm[nSrc] >> (7 - nByte));
        // 修改目标串的指针和计数值
        nDst++;
        // 修改字节计数值
        nByte++;
        // 到了一组的最后一个字节
        if (nByte == 7)
        {
            // 额外得到一个目标解码字节
            ascii[nDst] = nLeft;
            //ascii[nDst] = ConvertGSMToASCII(nLeft);
            // 修改目标串的指针和计数值
            nDst++;
            // 组内字节序号和残余数据初始化
            nByte = 0;
            nLeft = 0;
        }
        // 修改源串的指针和计数值
        nSrc++;
    }
    // 返回目标串长度
    return nDst;
}

/******************************************************************************/
/*@描述: 将8bit ASCII编码装换为GSM 7bit编码并保存到原地址中
 *
 *@参数: pSrc   待转换数据指针
 *       srcLen 待转换数据长度
 *
 *@返回: 转换后GSM 7bit编码总长度
 *@注意:
 *****************************************************************************/
int ASCII2GSM7bit(unsigned char *pSrc, int srcLen, int isLongSMS)
{
    int srcCnt = 0;             // the byte counter of src bytes
    int dstCnt = 0;             // the byte counter of coverted bytes
    unsigned char cvtCnt =
        0;   // the byte counter of converting byte-set(one byte-set include 8 bytes, 1 byte is a octets )
    unsigned char leftByte = 0; // the byte left by last covert
    unsigned char *pDst =
        pSrc; // the destination pointer to save coverted bytes.Save convert bytes to src
    unsigned char leftBit = 0x00;
    srcCnt = 0;
    dstCnt = 0;
    // all src bytes is divide to some byte-set, 1 byte-set include 8 octets(byte)
    // compress 8 octets to 7 septets, and save to source adrress
    while (srcCnt < srcLen)
    {
        // get the octets counter of converting byte-set
        cvtCnt = srcCnt & 0x07;
        // The first octets of one byte-set
        if (cvtCnt == 0)
        {
            leftByte = *pSrc; //dump it
        }
        else
        {
            // other octets of a byte-set
            *pDst = (*pSrc << (8 - cvtCnt)) + leftByte;
            if (isLongSMS)
            {
                unsigned char tmp = *pDst;
                *pDst = *pDst >> 1 | (leftBit << 7);
                leftBit = tmp & 0x01;
            }
            // right shift cvtCnt bit to remove the low cvtCnt bit
            leftByte = *pSrc >> cvtCnt;
            pDst++;
            dstCnt++;
        }
        pSrc++;
        srcCnt++;
    }
    //the left byte has been covert
    cvtCnt = srcCnt & 0x07;
    if (cvtCnt != 0)
    {
        *pDst = leftByte;
        if (isLongSMS)
        {
            unsigned char tmp = *pDst;
            *pDst = *pDst >> 1 | (leftBit << 7);
            leftBit = tmp & 0x01;
        }
        dstCnt++;
        pDst++;
    }
    *pDst = '\0';
    return dstCnt;
}

/******************************************************************************/
/*@描述: 将字符串形式的HEX数据转为字节形式的HEX数据
 *
 *@参数: string     待转换Hex串指针
 *       string_len 待转换Hex串长度
 *       hex        存放转换后的Hex数据的缓冲区
 *       hex_len    存放转换后的Hex数据的缓冲区长度
 *@返回: 转换后Hex长度
 *@注意:
 *****************************************************************************/
int StringHex2ByteHex(const char *string, int string_len, unsigned char *hex, \
    int hex_len)
{
    int i = 0;
    int j = 0;
    unsigned char high, low;
    for (i = 0, j = 0; i < string_len && j < hex_len; i += 2, j++)
    {
        high = string[i];
        low  = string[i + 1];
        if (high > '0' && high <= '9')
            high = high - '0';
        else if (high >= 'A' && high <= 'F')
            high = high - 'A' + 10;
        else if (high >= 'a' && high <= 'f')
            high = high - 'a' + 10;
        else
            high = 0;
        if (low > '0' && low <= '9')
            low = low - '0';
        else if (low >= 'A' && low <= 'F')
            low = low - 'A' + 10;
        else if (low >= 'a' && low <= 'f')
            low = low - 'a' + 10;
        else
            low = 0;
        hex[j] = ((high << 4) & 0XF0) | (low & 0XF);
    }
    return j;
}

/* Public function prototypes -----------------------------------------------*/
/**
 * @brief parse pdu string
 *
 * @param pdu_str
 *          such as:
 *              0891683110801105F0440D91685106087529F500009150410154822357050003FF0202CC7450D9FDAE9FD120FA1B242FBBC96539A85D0685D96C101D5D06CDCB727B7A5C062541F272BC9E969759A0B09B0C72BFE9A0B0591E4E93416F3388FD7697D96977793E778100
 *
 * @param cb    prase callback
 *
 * @return int >=0 :ok
 *              <0 :error
 */
int sms_pdu_parse(char *pdu_str, sms_rcv_callback_t cb)
{
    /**
     * PDU formate
     * Linker: https://www.cnblogs.com/deanding/p/7729385.html
     * -----------------------------------------------------------------------
     * | SCA    | PDU type |   OA  |   PID |   DCS |   SCTS    |   UDL |   UD
     * -----------------------------------------------------------------------
     * | 1~12   |   1      |2~12   |   1   |   1   |   7       |   1   |   n
     * -----------------------------------------------------------------------
     */
    sms_state_t state = SMS_REC_UNREAD;
    char *p = pdu_str;
    char hex[3]     = {0};
    char source[32] = {0};
    int  byte, type, cnt, long_sms;
    /**
     * SCA: 
     *  >短消息服务中心号码
     *  >长度 1-12B
     *  >length + type + address
     *      >>length = type + address，00时不提供后面部分
     *      >>type短信中心地址的类型
     *          >>>81:国内
     *          >>>A1:国内
     *          >>>91:国际
     *      >>address服务中心地址，对源地址做奇偶对调，如果源地址长度为奇数位末尾补F
    */
    if (!p || strlen(p) < 2)
    {
        LOG_W("PDU format error, PDU SCA length %d not enough", p ? strlen(p) : 0);
        return -1;
    }
    memcpy(hex, p, 2);
    p += 2;
    byte = strtol(hex, NULL, 16);
    if (!p || strlen(p) < byte)
    {
        LOG_W("PDU format error, PDU SCA body %d not enough", p ? strlen(p) : 0);
        return -1;
    }
    if (byte)
    {
        memcpy(hex, p, 2);
        p  += 2;
        cnt = 0;
        type = strtol(hex, NULL, 16);    
        if (0x91 == type)
        {
            source[cnt++] = '+';
        }    
        while (p && byte)
        {
            if ('F' != *p)
            {
                source[cnt++] = *(p + 1);
                source[cnt++] = *(p + 0);
            }
            else
            {
                source[cnt++] = *(p + 1);
                break;
            }
            p    += 2;
            byte -= 1;
        }
        LOG_I("SMS from center: %s", source);
        p += 2;
    }  
    else
    {
        LOG_I("SMS from center: Not provided");
    }  
    /**
     * PDU-Type: 
     *  >协议数据单元类型
     *  >长度 1B
     *  >8bit位图
     *      >> -------------------------------------------------
     *        |bit No. |7    |6   |5   |4   |3   |2   |1   |0   |
     *        |sender  |RP   |UDHI|SRR |VPF |VPF |RD  |MTI |MTI |
     *        |receiver|RP   |UDHI|SRI |\   |\   |MMS |MTI |MTI |
     *         -------------------------------------------------
     *          >>>参考Linker 
    */
    if (!p || strlen(p) < 2)
    {
        LOG_W("PDU format error, PDU Type length %d not enough", p ? strlen(p) : 0);
        return -1;
    }
    memcpy(hex, p ,2);
    p += 2;
    byte = strtol(hex, NULL, 16);
    long_sms = byte & SMS_RESP_UHDI;
    /**
     * OA/DA: 
     *  >OA:发送方地址
     *  >DA:接收方地址
     *  >长度 2-12B
     *  >type + address
     *      >>type短信中心地址的类型
     *          >>>81:国内
     *          >>>A1:国内
     *          >>>91:国际
     *      >>address发送或接收地址，对源地址做奇偶对调，如果源地址长度为奇数位末尾补F
    */
    if (!p || strlen(p) < 2)
    {
        LOG_W("PDU format error, PDU Sender length %d not enough", p ? strlen(p) : 0);
        return -1;
    }
    memcpy(hex, p, 2);
    p += 2;
    byte = strtol(hex, NULL, 16);
    byte = (2 + (byte + 1)) / 2;
    memcpy(hex, p, 2);
    p  += 2;
    cnt = 0;
    type = strtol(hex, NULL, 16);
    memset(source, 0, sizeof(source));
    if (0x91 == type)
    {
        source[cnt++] = '+';
    }
    while (p && byte)
    {
        if ('F' != *p)
        {
            source[cnt++] = *(p + 1);
            source[cnt++] = *(p + 0);
        }
        else
        {
            source[cnt++] = *(p + 1);
            break;
        }
        p    += 2;
        byte -= 1;
    }
    LOG_I("SMS from sender: %s", source);
    p += 2;
    /**
     * PID: 
     *  >消息中心处理消息方式
     *  >FAX
     *  >Voice
    */
    if (!p || strlen(p) < 2)
    {
        LOG_W("PDU format error, PDU PID length %d not enough", p ? strlen(p) : 0);
        return -1;
    }
    memcpy(hex, p, 2);
    p += 2;
    byte = strtol(hex, NULL, 16);
    /**
     * DCS: 
     *  >用户数据编码方式
    */
    sms_pdu_dcs_t dcs    = PDU_DCS_GSM;
    sms_encode_t  encode = SMS_ENCODE_TEXT;
    if (!p || strlen(p) < 2)
    {
        LOG_W("PDU format error, PDU DCS length %d not enough", p ? strlen(p) : 0);
        return -1;
    }
    memcpy(hex, p, 2);
    p += 2;
    byte = strtol(hex, NULL, 16);
    if (0xF3 == (byte | 0xF3))
    {
        dcs = PDU_DCS_GSM;
    }
    else if (byte & 0x04)
    {
        dcs = PDU_DCS_8BIT;
    }
    else if (byte & 0x08)
    {
        dcs = PDU_DCS_UCS2;
    }
    else
    {
        LOG_W("SMS TP-DCS error: %02X", byte);
        dcs = (sms_pdu_dcs_t)byte;
    }
    LOG_I("SMS TP-DCS     : %s",                                              \
        PDU_DCS_GSM  == dcs ? "GSM 7Bit" :                                    \
        PDU_DCS_8BIT == dcs ? "8 Bit" :                                       \
        PDU_DCS_UCS2 == dcs ? "UCS2" : "unknown");
    if (PDU_DCS_GSM == dcs || PDU_DCS_8BIT == dcs)
    {
        encode = SMS_ENCODE_TEXT;
    }
    else
    {
        encode = SMS_ENCODE_UCS2;
    }
    /**
     * SCTS: 
     *  >时间戳
    */
    if (!p || strlen(p) < 14)
    {
        LOG_W("PDU format error, PDU SCTS length %d not enough", p ? strlen(p) : 0);
        return -1;
    }
    LOG_I("SMS Center TS  : 20%c%c/%c%c/%c%c %c%c:%c%c:%c%c",
        *(p + 1), *(p), *(p + 3), *(p + 2), *(p + 5), *(p + 4), *(p + 7), *(p + 6),
        *(p + 9), *(p + 8), *(p + 11), *(p + 10));
    p += 14;
    /**
     * UDL: 
     *  >用户数据长度
    */
    int udl, udh;
    if (!p || strlen(p) < 2)
    {
        LOG_W("PDU format error, PDU UDL length %d not enough", p ? strlen(p) : 0);
        return -1;
    }
    memcpy(hex, p, 2);
    p += 2;
    byte = strtol(hex, NULL, 16);
    udl  = byte;
    LOG_I("SMS UDL        : %dB %s message", udl, long_sms ? "long" : "normal");
    /**
     * UD: 
     *  >用户数据
    */
    char ud[512] = {0};
    char tp[512] = {0};
    if (!p || strlen(p) < udl)
    {
        LOG_W("PDU format error, PDU UD length %d not enough", p ? strlen(p) : 0);
        return -1;
    }
    if (long_sms)
    {
        if (strlen(p) < 12)
        {
            LOG_W("PDU format error, PDU UD header length %d not enough", p ? strlen(p) : 0);
            return -1;
        }
        memcpy(hex, p, 2);
        p += 2;
        udh = strtol(hex, NULL, 16);
        if (strlen(p) < udh * 2 || udh < 5)
        {
            LOG_W("PDU format error, PDU UD header length %d error", p ? strlen(p) : 0);
            return -1;
        }
        memcpy(ud, p - 2, (udh + 1) * 2);
        p += udh * 2 -4;
        memcpy(hex, p ,2);
        p += 2;
        byte = strtol(hex, NULL, 16);
        memcpy(hex, p, 2);
        p += 2;
        long_sms = strtol(hex, NULL, 16);
        LOG_I("SMS LONG HEADER: %s %d|%d(current|total)", ud, long_sms, byte);
    }
    if (!p || strlen(p) < udl)
    {
        LOG_W("PDU format error, PDU UD length %d error", p ? strlen(p) : 0);
        return -1;
    }
    memset(ud, 0, sizeof(ud));
    if (strstr(p, SMS_RESP_HEADER))
    {
        sscanf(p, "%s+CMGL: ", tp);
    }
    else
    {
        memcpy(tp, p, strlen(p));
    } 
    cnt = 0;
    while (cnt < strlen(tp) / 2)
    {
        memcpy(hex, (char *)&tp[cnt * 2], 2);
        byte = strtol(hex, NULL, 16);
        ud[cnt++] = byte;
    }  
    udl = cnt; 
    if (PDU_DCS_GSM == dcs)
    {
        memset(tp, 0, sizeof(tp));
        memcpy(tp, ud, udl);
        memset(ud, 0, sizeof(ud));
        if (long_sms)
        {
            ud[0] = tp[0] >> (7 - (udh + 1));
            udl = GSM7bit2ASCII(tp + 1, udl - 1, ud + 1, sizeof(ud) - 1);
            udl += 1;
        }
        else
        {
            udl = GSM7bit2ASCII(tp, udl, ud, sizeof(ud));
        }
    }
    if (udl < sizeof(ud))
    {
        ud[udl] = '\0';
    }
    if (cb && udl > 0)
    {
        return cb(ud, udl, source, encode, state);
    }  
    return udl;
}
/* End of file****************************************************************/
